package com.pinkpig.excel;


import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.pinkpig.excel.ext.address.CellAddressReferenceStrategy;
import com.pinkpig.excel.ext.merge.FlexibleAbsoluteMergeStrategy;
import com.pinkpig.excel.ext.merge.FlexibleAbsoluteMergeStrategy.FlexibleMergeIndex;
import com.pinkpig.excel.ext.style.HorizontalCellStyleRefreshableStrategy;
import com.pinkpig.excel.ext.style.MultiColumnWidthStyleStrategy;
import com.pinkpig.excel.metadata.DatabaseTableParser;
import com.pinkpig.excel.metadata.DoubleTableMetaData;
import com.pinkpig.excel.metadata.EmptyMetaData;
import com.pinkpig.excel.metadata.TableMetaData;
import com.pinkpig.excel.row.RowModelBuilder;
import com.pinkpig.excel.row.RowModelBuilderFactory;
import com.pinkpig.excel.row.model.*;
import com.pinkpig.excel.xml.config.TableExportConfiguration;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author vincenfeng
 */
public class TableExportDelegate {

    private TableExportConfiguration configuration;

    private DatabaseTableParser databaseTableParser;

    private ExcelWriter excelWriter;

    private String FILE_NAME = "TABLE_DEFINITON";

    private WriteCellStyle contentCellType;

    private HorizontalCellStyleRefreshableStrategy horizontalCellStyleStrategy;

    private CellAddressReferenceStrategy cellAddressReferenceStrategy;

    private WriteFont boldFont = new WriteFont();

    private int writeSheetNo = 0;

    public TableExportDelegate(TableExportConfiguration configuration, DatabaseTableParser databaseTableParser, String fileName){
        this.configuration = configuration;
        this.databaseTableParser = databaseTableParser;
        this.FILE_NAME = fileName;
        this.contentCellType = TableExportHelper.newEmptyCellType();
        this.horizontalCellStyleStrategy = new HorizontalCellStyleRefreshableStrategy(null, contentCellType);
        this.cellAddressReferenceStrategy = new CellAddressReferenceStrategy(databaseTableParser.getAllTableName());
        this.boldFont.setBold(true);
        convertLabelCellType();
    }

    public void generateExcel(){
        generateOverview();
    }

    /**
     * 生成homepage总览
     */
    private void generateOverview(){
        FlexibleAbsoluteMergeStrategy.FlexibleMergeIndex flexibleMergeIndex = new FlexibleAbsoluteMergeStrategy.FlexibleMergeIndex(-1,-1,-1,-1);
        excelWriter = EasyExcel.write(FILE_NAME)
                .registerWriteHandler(horizontalCellStyleStrategy)
                .registerWriteHandler(new FlexibleAbsoluteMergeStrategy(flexibleMergeIndex))
                .registerWriteHandler(cellAddressReferenceStrategy)
                .build();
        WriteSheet writeSheet = TableExportHelper.newWriteSheet(writeSheetNo++,"总览", getOverviewWriteHandler());
        RowModelBuilder builder = RowModelBuilderFactory.getBuilder(TableHeadRowModel.class);
        writeLabel(builder.build(null), writeSheet);
        builder = RowModelBuilderFactory.getBuilder(null);
        writeEmpty(builder.build(new EmptyMetaData(1)), writeSheet);
        AtomicInteger writeRowIndex = new AtomicInteger(2);
        configuration.getTableExportContext().getCategoryTablesMap().keySet().forEach(k->{
            List<String> tableNames = configuration.getTableExportContext().getCategoryTablesMap().get(k);
            List<TableMetaData> tableMetaDatas = new ArrayList<>();
            tableNames.forEach(t->{
                if(null!=databaseTableParser.getTableMetaData(t)){
                    tableMetaDatas.add(databaseTableParser.getTableMetaData(t));
                }
            });
            generateOneCategory(writeSheet, tableMetaDatas, writeRowIndex, flexibleMergeIndex);
        });
        //生成其他模块表定义
        List<TableMetaData> otherTableMetaDatas = databaseTableParser.getAllTableMetaData().stream().filter(t->configuration.getTableExportContext().getCategory(t.getTableName())==null).collect(Collectors.toList());
        generateOneCategory(writeSheet, otherTableMetaDatas, writeRowIndex, flexibleMergeIndex);
        excelWriter.finish();
    }

    private void generateOneCategory(WriteSheet homeSheet, List<TableMetaData> tableMetaDatas, AtomicInteger writeRowIndex, FlexibleMergeIndex flexibleMergeIndex){
        if(null==tableMetaDatas || tableMetaDatas.size()<=0){
            return;
        }
        flexibleMergeIndex.setFirstRowIndex(writeRowIndex.get());
        flexibleMergeIndex.setLastRowIndex(writeRowIndex.get()+(tableMetaDatas.size()-1)/2);
        flexibleMergeIndex.setFirstColumnIndex(0);
        flexibleMergeIndex.setLastColumnIndex(0);
        for(int i=0;i<tableMetaDatas.size();i++){
            TableMetaData tableMetaData1 = tableMetaDatas.get(i++);
            TableMetaData tableMetaData2 = i<tableMetaDatas.size()?tableMetaDatas.get(i):null;
            RowModelBuilder builder2 = RowModelBuilderFactory.getBuilder(TableRowModel.class);
            writeContent(builder2.build(new DoubleTableMetaData(tableMetaData1, tableMetaData2)), homeSheet);
        }
        writeRowIndex.set(writeRowIndex.get()+(tableMetaDatas.size()+1)/2);
        flexibleMergeIndex.clean();
        generateTableDefinition(tableMetaDatas.get(0).getCategory(), tableMetaDatas);
    }

    private void generateTableDefinition(String category, List<TableMetaData> tableMetaDatas){
        if(tableMetaDatas.isEmpty()) {
            return;
        }
        WriteSheet writeSheet = TableExportHelper.newWriteSheet(writeSheetNo++,category, getTableDefinitionWriteHandler());
        tableMetaDatas.forEach(t->{
            RowModelBuilder builder = RowModelBuilderFactory.getBuilder(ColumnTableHeadModel.class);
            writeBoldLabel(builder.build(t), writeSheet);
            builder = RowModelBuilderFactory.getBuilder(ColumnHeadRowModel.class);
            writeBoldLabel(builder.build(null), writeSheet);
            builder = RowModelBuilderFactory.getBuilder(ColumnRowModel.class);
            writeContent(builder.build(t), writeSheet);
            builder = RowModelBuilderFactory.getBuilder(IndexHeadRowModel.class);
            writeBoldContent(builder.build(null), writeSheet);
            builder = RowModelBuilderFactory.getBuilder(IndexRowModel.class);
            writeContent(builder.build(t), writeSheet);
            builder = RowModelBuilderFactory.getBuilder(null);
            writeEmpty(builder.build(new EmptyMetaData(2)), writeSheet);
        });
    }

    private void write(List data, WriteSheet writeSheet){
        excelWriter.write(data, writeSheet);
    }

    private void writeBoldLabel(List data, WriteSheet writeSheet){
        WriteCellStyle labelCellStyle = convertLabelCellType();
        labelCellStyle.setWriteFont(boldFont);
        horizontalCellStyleStrategy.refreshCellStyle();
        excelWriter.write(data, writeSheet);
    }

    private void writeLabel(List data, WriteSheet writeSheet){
        WriteCellStyle labelCellStyle = convertLabelCellType();
        labelCellStyle.setWriteFont(null);
        horizontalCellStyleStrategy.refreshCellStyle();
        excelWriter.write(data, writeSheet);
    }

    private void writeContent(List data, WriteSheet writeSheet){
        WriteCellStyle labelCellStyle = convertContentCellType();
        labelCellStyle.setWriteFont(null);
        horizontalCellStyleStrategy.refreshCellStyle();
        excelWriter.write(data, writeSheet);
    }

    private void writeBoldContent(List data, WriteSheet writeSheet){
        WriteCellStyle labelCellStyle = convertContentCellType();
        labelCellStyle.setWriteFont(boldFont);
        horizontalCellStyleStrategy.refreshCellStyle();
        excelWriter.write(data, writeSheet);
    }

    private void writeEmpty(List data, WriteSheet writeSheet){
        cleanStyle();
        excelWriter.write(data, writeSheet);
    }

    private WriteCellStyle convertLabelCellType(){
        WriteCellStyle labelCellStyle = contentCellType;
        labelCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
        labelCellStyle.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
        setBorder();
        return labelCellStyle;
    }

    private WriteCellStyle convertContentCellType(){
        contentCellType.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
        contentCellType.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        setBorder();
        return contentCellType;
    }

    private void setBorder(){
        contentCellType.setBorderLeft(BorderStyle.THIN);
        contentCellType.setBorderRight(BorderStyle.THIN);
        contentCellType.setBorderTop(BorderStyle.THIN);
        contentCellType.setBorderBottom(BorderStyle.THIN);
        contentCellType.setTopBorderColor(IndexedColors.BLACK.getIndex());
        contentCellType.setRightBorderColor(IndexedColors.BLACK.getIndex());
        contentCellType.setBottomBorderColor(IndexedColors.BLACK.getIndex());
        contentCellType.setLeftBorderColor(IndexedColors.BLACK.getIndex());
    }

    private void cleanStyle(){
        contentCellType.setFillForegroundColor(null);
        contentCellType.setFillPatternType(null);
        contentCellType.setBorderLeft(null);
        contentCellType.setBorderRight(null);
        contentCellType.setBorderTop(null);
        contentCellType.setBorderBottom(null);
        contentCellType.setTopBorderColor(null);
        contentCellType.setRightBorderColor(null);
        contentCellType.setBottomBorderColor(null);
        contentCellType.setLeftBorderColor(null);
        contentCellType.setWriteFont(null);
    }

    private List<WriteHandler> getOverviewWriteHandler(){
        List<WriteHandler> list = new ArrayList<>();
        Map<Integer, Integer> columnsWidth = new HashMap<>();
        columnsWidth.put(0, 25);
        columnsWidth.put(1, 30);
        columnsWidth.put(2, 30);
        columnsWidth.put(3, 30);
        columnsWidth.put(4, 30);
        MultiColumnWidthStyleStrategy styleStrategy = new MultiColumnWidthStyleStrategy(columnsWidth);
        list.add(styleStrategy);
        return list;
    }

    private List<WriteHandler> getTableDefinitionWriteHandler(){
        List<WriteHandler> list = new ArrayList<>();
        Map<Integer, Integer> columnsWidth = new HashMap<>();
        columnsWidth.put(0, 25);
        columnsWidth.put(1, 25);
        columnsWidth.put(2, 20);
        columnsWidth.put(3, 15);
        columnsWidth.put(4, 15);
        columnsWidth.put(5, 25);
        MultiColumnWidthStyleStrategy styleStrategy = new MultiColumnWidthStyleStrategy(columnsWidth);
        list.add(styleStrategy);
        return list;
    }

}
